use serde::{Deserialize, Serialize};
use ts_rs::TS;

/// 定义一个结构体来表示二维空间中的点来抽象表示股票的时间及价格，指标的时间及数值
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct Point {
    /// 股价日期
    pub x: f64,
    /// 股价，k线收盘价,最高价，最低价，开盘价 OHLC
    pub y: f64,
}

impl Point {
    /// To display the data on a canvas with a height of e.g. 100 units,
    /// where the highest value is at the top and the lowest at the bottom,
    /// you'll need to scale and map the values to the canvas height.
    pub fn scale_y(mut self, min_value: f64, max_value: f64, canvas_height: f64) -> Self {
        self.y = ((self.y - min_value) / (max_value - min_value)) * canvas_height;
        self
    }
    pub fn scale_x(mut self, min_value: f64, max_value: f64, canvas_height: f64) -> Self {
        self.x = ((self.x - min_value) / (max_value - min_value)) * canvas_height;
        self
    }

    /// 计算两条线在交点B处的夹角（以度为单位）line: base->base_before and base->base_after
    ///
    /// base_before: Point
    ///
    /// base: Point
    ///
    /// base_after: Point
    ///
    /// Return : 负数表示股价向下反转，正数表示股价向上反转
    ///
    pub fn calculate_angle_at_base(base_before: &Point, base: &Point, base_after: &Point) -> f64 {
        let vector_pa = Point {
            x: if base_before.x - base.x == 0.0 {
                -1.0
            } else {
                base_before.x - base.x
            },
            y: base_before.y - base.y,
        };
        let vector_pc = Point {
            x: if base_after.x - base.x == 0.0 {
                1.0
            } else {
                base_after.x - base.x
            },
            y: base_after.y - base.y,
        };
        // println!("{:?}-{:?}-{:?} vector_pa= {:?} vector_pc={:?}", base_before,base,base_after,vector_pa, vector_pc);
        let dot_product = Self::dot_product(&vector_pa, &vector_pc);
        let magnitude_product =
            Self::vector_magnitude(&vector_pa) * Self::vector_magnitude(&vector_pc);

        // 计算夹角的余弦值
        let cos_theta = dot_product / magnitude_product;

        // 计算夹角（反余弦函数），并转换为度数
        let theta_rad = f64::acos(cos_theta);
        let theta_deg = theta_rad * (180.0 / std::f64::consts::PI);

        // 返回夹角（以度为单位）
        theta_deg * if base.y > base_before.y { -1f64 } else { 1f64 }
    }

    // 计算两个向量的点积
    fn dot_product(v1: &Point, v2: &Point) -> f64 {
        v1.x * v2.x + v1.y * v2.y
    }

    // 计算一个向量的模
    fn vector_magnitude(v: &Point) -> f64 {
        f64::sqrt(v.x * v.x + v.y * v.y)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_calculate_angle_at_base() {
        // 定义两条直线上的点，其中B是交点
        let base_before = Point { x: -1.0, y: 0.0 };

        let base_1 = Point { x: 0.0, y: 1.0 }; // 交点B1
        let base_2 = Point { x: 0.0, y: -1.0 }; // 交点B2

        let base_3 = Point { x: 0.0, y: 0.5 }; // 交点B3
        let base_4 = Point { x: 0.0, y: -0.5 }; // 交点B4

        let base_5 = Point { x: 0.0, y: 1.5 }; // 交点B5
        let base_6 = Point { x: 0.0, y: -1.5 }; // 交点B6

        let base_after = Point { x: 1.0, y: 0.0 };

        // 计算夹角
        let angle_deg1 = Point::calculate_angle_at_base(&base_before, &base_1, &base_after);
        let angle_deg2 = Point::calculate_angle_at_base(&base_before, &base_2, &base_after);
        let angle_deg3 = Point::calculate_angle_at_base(&base_before, &base_3, &base_after);
        let angle_deg4 = Point::calculate_angle_at_base(&base_before, &base_4, &base_after);
        let angle_deg5 = Point::calculate_angle_at_base(&base_before, &base_5, &base_after);
        let angle_deg6 = Point::calculate_angle_at_base(&base_before, &base_6, &base_after);

        println!(
            "The angle between line base-a and base-c at  B1 is: {:.2} degrees",
            angle_deg1
        );
        println!(
            "The angle between line base-a and base-c at  B2 is: {:.2} degrees",
            angle_deg2
        );
        println!(
            "The angle between line base-a and base-c at  B3 is: {:.2} degrees",
            angle_deg3
        );
        println!(
            "The angle between line base-a and base-c at  B4 is: {:.2} degrees",
            angle_deg4
        );
        println!(
            "The angle between line base-a and base-c at  B5 is: {:.2} degrees",
            angle_deg5
        );

        println!(
            "The angle between line base-a and base-c at  B5 is: {:.2} degrees",
            angle_deg6
        );
    }
}
